/*
 *  symbol.cpp
 *  plasticine
 *
 *  Created by Vincent on 1/13/10.
 *  Copyright 2010 __MyCompanyName__. All rights reserved.
 *
 */

#include "symbol.h"
#include "opcode.h"
#include <iostream>
#include <cassert>

using namespace std;

class ConstantPool;

/// global symbol table
SymbolTable symbolTable;

/// current scope when entering symbols
Scope*  Scope::curScope;

Symbol::Symbol(const char *sym){
    name = new string(sym);
}

Symbol::Symbol(const string *sym){
    name = new string(*sym);
}

Symbol::~Symbol(){
    delete name;
}

void Symbol::printInfo(){
    cout<<"[Symbol: "<<*name<<"]"<<endl;
}

int Symbol::getLocal(){
    return NOLOCAL;
}

int Symbol::getPoolIndex(){
    return NOINDEX;
}

int VarSymbol::getLocal(){
    return localPos;
}

void VarSymbol::printInfo(){
    cout<<"[VarSymbol:"<<getName()<<" local:"<<localPos<<"]"<<endl;
}



Scope::Scope(Scope *s){
    upward = s;
}

Scope::~Scope(){

}

void Scope::insertSymbol(Symbol *sym){
    symbols.insert(make_pair(sym->getName(),sym));
}


Symbol* Scope::getSymbol(string *str){
    multimap<string,Symbol*>::iterator it;

    for(it = symbols.lower_bound(*str);it != symbols.upper_bound(*str);++it){
        //if(it->second == str))
            return it->second;
    }

    return 0;
}

void Scope::printInfo(){
    multimap<string,Symbol*>::iterator it;

    for(it = symbols.begin();it != symbols.end();++it){
        it->second->printInfo();
    }
}

SymbolTable::SymbolTable(){

}

SymbolTable::~SymbolTable(){

}

void SymbolTable::insertSymbol(Symbol *sym){
    cout<<"insert symbol:";
    sym->printInfo();
    Scope::curScope->insertSymbol(sym);
}

/// search a symbol from current scope of ctx and upward
Symbol* SymbolTable::getSymbol(string *sym,const Context *ctx){
    Scope *scp = Scope::curScope;
    Symbol *result;

    assert(scp);
    while(scp){
        result = scp->getSymbol(sym);
        if(result){
            return result;
        }
        scp = scp->getUpward();
    }

    return 0;
}


void SymbolTable::printInfo(){

}

void SymbolEnter::visitVariableExpr(VariableExpr& expr){
    Symbol *sym = symbolTable.getSymbol(expr.name,Context::curContext);

    if(!sym){
        //cout<<"insert to symboltable"<<endl;
        sym = new VarSymbol(expr.name,Context::curContext->nextSlot++);
        symbolTable.insertSymbol(sym);
    }

    //TODO: refactor here
    expr.varSym = dynamic_cast<VarSymbol*>(sym);
    assert(expr.varSym);
}

void SymbolEnter::visitFunctionNameExpr(FunctionNameExpr& expr){
    Symbol *sym = symbolTable.getSymbol(expr.name,Context::curContext);

    if(!sym){
        // treat the function object as first-class value,
        // so it takes up a slot as local variable
        FunctionSymbol *funcSym = new FunctionSymbol(expr.name,Context::curContext->nextSlot++);
        funcSym->funcName = &expr;
        expr.funcSym = funcSym;
        symbolTable.insertSymbol(funcSym);
    }
}

void SymbolEnter::visitConstantExpr(ConstantExpr &expr){
    // change here if new constant type to be added
    switch(expr.type){
    case ConstantExpr::CONST_INTEGER:
        expr.constSym = ConstantPool::createInteger(expr.value);
        break;
    case ConstantExpr::CONST_STRING:
        expr.constSym = ConstantPool::createString(expr.value);
        break;
    default:
        assert(0 && "Unknown Constant type");
    }
}

void SymbolEnter::visitBinaryExpr(BinaryExpr& expr){
    expr.left->accept(this);
    expr.right->accept(this);
}

void SymbolEnter::visitVariableDeclare(VariableDeclareStatement& stmt){
    stmt.varExpr->accept(this);
}

void SymbolEnter::visitAssignment(AssignmentStatement& stmt){
    stmt.lhs->accept(this);
    stmt.rhs->accept(this);
}

void SymbolEnter::visitParameterList(ParameterListStatement& stmt){
    vector<VariableExpr*>::iterator it;

    for(it = stmt.variables.begin();it != stmt.variables.end();it++){
        (*it)->accept(this);
    }
}

void SymbolEnter::visitFunctionDef(FunctionDefStatement& stmt){
    Context *oldctx;

    // visit the function name expr first in the old context
    stmt.funcName->accept(this);

    // then go into the new context for this function
    oldctx = Context::curContext;
    stmt.funcName->funcSym->context = new Context(stmt.funcName->funcSym);
    Context::curContext = stmt.funcName->funcSym->context;

    if(stmt.paraList){
        stmt.paraList->accept(this);
    }
    stmt.funcBody->accept(this);

    // leave this function definition, restore the context
    Context::curContext = oldctx;
}

void SymbolEnter::visitFunctionCall(FunctionCallStatement& stmt){
    Symbol *sym = symbolTable.getSymbol(stmt.funcName->name,Context::curContext);

    // the function symbol should be entered in the FunctionNameExpr visit
    // TODO: add undefined set to eliminate the restriction
    assert(sym && "don't support use before definition of a function right now");

    FunctionSymbol *funcSym = dynamic_cast<FunctionSymbol*>(sym);
    // as a first-class variable, other variables' name can't conflict this funcName
    assert(funcSym && "the function calling must own a function symbol");
    stmt.funcName->funcSym = funcSym;

    stmt.exprList->accept(this);
}

void SymbolEnter::visitExprList(ExprListStatement& stmt){
    vector<Expr*>::iterator it;

    for(it = stmt.variables.begin();it != stmt.variables.end();it++){
        // TODO: constant arguments support
        VariableExpr *vexpr = dynamic_cast<VariableExpr*>(*it);
        assert(vexpr && "only support variables as arguments");
        Symbol *sym = symbolTable.getSymbol(vexpr->name,Context::curContext);
        VarSymbol *vsym = dynamic_cast<VarSymbol*>(sym);
        assert(vsym && "undefined variable");
        vexpr->varSym = vsym;
    }
}

void SymbolEnter::visitBlock(Block &blk){
    Scope *oldscp,*scp;
    vector<Tree*>::iterator it;

    oldscp = Scope::curScope;
    scp = new Scope(oldscp);
    blk.blockSym = new BlockSymbol(new string("block"),scp);

    Scope::curScope = scp;
    for(it = blk.statements.begin();it != blk.statements.end();it++){
        (*it)->accept(this);
    }
    Scope::curScope = oldscp;
}

void SymbolEnter::visitWhileStatement(WhileStatement& stmt){
    assert(stmt.expr && "while statement without expr");

    stmt.expr->accept(this);

    if(stmt.whileBody){
        stmt.whileBody->accept(this);
    }
}

void SymbolEnter::visitIfStatement(IfStatement &stmt){
    assert(stmt.expr && "if-statment without expr");

    stmt.expr->accept(this);

    if(stmt.ifBlock){
        stmt.ifBlock->accept(this);
    }

    if(stmt.elseBlock){
        stmt.elseBlock->accept(this);
    }
}

